<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>part2_6_simplecache - learning-gem5</title>
        <!-- Custom HTML head -->
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="../favicon.svg">
        <link rel="shortcut icon" href="../favicon.png">
        <link rel="stylesheet" href="../css/variables.css">
        <link rel="stylesheet" href="../css/general.css">
        <link rel="stylesheet" href="../css/chrome.css">
        <link rel="stylesheet" href="../css/print.css" media="print">
        <!-- Fonts -->
        <link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="../fonts/fonts.css">
        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="../highlight.css">
        <link rel="stylesheet" href="../tomorrow-night.css">
        <link rel="stylesheet" href="../ayu-highlight.css">

        <!-- Custom theme stylesheets -->
    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "../";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded affix "><a href="../part0_introduction.html">Learning gem-5</a></li><li class="chapter-item expanded "><a href="../part0_introduction.html"><strong aria-hidden="true">1.</strong> part0_introduction</a></li><li class="chapter-item expanded "><a href="../part1/part1_1_building.html"><strong aria-hidden="true">2.</strong> part1</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part1/part1_1_building.html"><strong aria-hidden="true">2.1.</strong> part1_1_building</a></li><li class="chapter-item expanded "><a href="../part1/part1_2_simple_config.html"><strong aria-hidden="true">2.2.</strong> part1_2_simple_config</a></li><li class="chapter-item expanded "><a href="../part1/part1_3_cache_config.html"><strong aria-hidden="true">2.3.</strong> part1_3_cache_config</a></li><li class="chapter-item expanded "><a href="../part1/part1_4_gem5_stats.html"><strong aria-hidden="true">2.4.</strong> part1_4_gem5_stats</a></li><li class="chapter-item expanded "><a href="../part1/part1_5_gem5_example_configs.html"><strong aria-hidden="true">2.5.</strong> part1_5_gem5_example_configs</a></li><li class="chapter-item expanded "><a href="../part1/part1_6_extending_configs.html"><strong aria-hidden="true">2.6.</strong> part1_6_extending_configs</a></li></ol></li><li class="chapter-item expanded "><a href="../part2/part2_0_environment.html"><strong aria-hidden="true">3.</strong> part2</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part2/part2_0_environment.html"><strong aria-hidden="true">3.1.</strong> part2_0_environment</a></li><li class="chapter-item expanded "><a href="../part2/part2_1_helloobject.html"><strong aria-hidden="true">3.2.</strong> part2_1_helloobject</a></li><li class="chapter-item expanded "><a href="../part2/part2_2_debugging.html"><strong aria-hidden="true">3.3.</strong> part2_2_debugging</a></li><li class="chapter-item expanded "><a href="../part2/part2_3_events.html"><strong aria-hidden="true">3.4.</strong> part2_3_events</a></li><li class="chapter-item expanded "><a href="../part2/part2_4_parameters.html"><strong aria-hidden="true">3.5.</strong> part2_4_parameters</a></li><li class="chapter-item expanded "><a href="../part2/part2_5_memoryobject.html"><strong aria-hidden="true">3.6.</strong> part2_5_memoryobject</a></li><li class="chapter-item expanded "><a href="../part2/part2_6_simplecache.html" class="active"><strong aria-hidden="true">3.7.</strong> part2_6_simplecache</a></li><li class="chapter-item expanded "><a href="../part2/part2_7_arm_power_modelling.html"><strong aria-hidden="true">3.8.</strong> part2_7_arm_power_modelling</a></li><li class="chapter-item expanded "><a href="../part2/part2_8_arm_dvfs_support.html"><strong aria-hidden="true">3.9.</strong> part2_8_arm_dvfs_support</a></li></ol></li><li class="chapter-item expanded "><a href="../part3/part3_00_MSIntro.html"><strong aria-hidden="true">4.</strong> part3</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="../part3/part3_00_MSIntro.html"><strong aria-hidden="true">4.1.</strong> part3_00_MSIntro</a></li><li class="chapter-item expanded "><a href="../part3/part3_01_cache-intro.html"><strong aria-hidden="true">4.2.</strong> part3_01_cache-intro</a></li><li class="chapter-item expanded "><a href="../part3/part3_02_cache-declarations.html"><strong aria-hidden="true">4.3.</strong> part3_02_cache-declarations</a></li><li class="chapter-item expanded "><a href="../part3/part3_03_cache-in-ports.html"><strong aria-hidden="true">4.4.</strong> part3_03_cache-in-ports</a></li><li class="chapter-item expanded "><a href="../part3/part3_04_cache_actions.html"><strong aria-hidden="true">4.5.</strong> part3_04_cache_actions</a></li><li class="chapter-item expanded "><a href="../part3/part3_05_cache_transitions.html"><strong aria-hidden="true">4.6.</strong> part3_05_cache_transitions</a></li><li class="chapter-item expanded "><a href="../part3/part3_06_directory.html"><strong aria-hidden="true">4.7.</strong> part3_06_directory</a></li><li class="chapter-item expanded "><a href="../part3/part3_07_MSIbuilding.html"><strong aria-hidden="true">4.8.</strong> part3_07_MSIbuilding</a></li><li class="chapter-item expanded "><a href="../part3/part3_08_configuration.html"><strong aria-hidden="true">4.9.</strong> part3_08_configuration</a></li><li class="chapter-item expanded "><a href="../part3/part3_09_running.html"><strong aria-hidden="true">4.10.</strong> part3_09_running</a></li><li class="chapter-item expanded "><a href="../part3/part3_10_MSIdebugging.html"><strong aria-hidden="true">4.11.</strong> part3_10_MSIdebugging</a></li><li class="chapter-item expanded "><a href="../part3/part3_11_simple-MI_example.html"><strong aria-hidden="true">4.12.</strong> part3_11_simple-MI_example</a></li></ol></li><li class="chapter-item expanded "><a href="../part4_gem5_101.html"><strong aria-hidden="true">5.</strong> part4_gem5_101</a></li><li class="chapter-item expanded "><a href="../http://doxygen.gem5.org/develop/index.html"><strong aria-hidden="true">6.</strong> part4_gem5_102</a></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">learning-gem5</h1>

                    <div class="right-buttons">
                        <a href="../print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>
                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>
                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="创建一个简单的缓存对象"><a class="header" href="#创建一个简单的缓存对象">创建一个简单的缓存对象</a></h1>
<p>在本章中，我们将采用我们在<a href="https://www.gem5.org/documentation/learning_gem5/part2/memoryobject">上一章中</a>创建的内存对象框架，并为其添加缓存逻辑。</p>
<h2 id="简单缓存模拟对象"><a class="header" href="#简单缓存模拟对象">简单缓存模拟对象</a></h2>
<p>创建 SConscript （您可以<a href="https://www.gem5.org/_pages/static/scripts/part2/simplecache/SConscript">在此处</a>下载 ）文件后，我们可以创建 SimObject Python 文件。我们将管这个简单的内存对象叫 <code>SimpleCache</code>并在 <code>src/learning_gem5/simple_cache</code>创建这个 SimObject 文件。</p>
<pre><code class="language-python">from m5.params import *
from m5.proxy import *
from MemObject import MemObject

class SimpleCache(MemObject):
    type = 'SimpleCache'
    cxx_header = &quot;learning_gem5/simple_cache/simple_cache.hh&quot;

    cpu_side = VectorSlavePort(&quot;CPU side port, receives requests&quot;)
    mem_side = MasterPort(&quot;Memory side port, sends requests&quot;)

    latency = Param.Cycles(1, &quot;Cycles taken on a hit or to resolve a miss&quot;)

    size = Param.MemorySize('16kB', &quot;The size of the cache&quot;)

    system = Param.System(Parent.any, &quot;The system this cache is part of&quot;)
</code></pre>
<p>和<a href="https://www.gem5.org/documentation/learning_gem5/part2/memoryobject">上一章的</a>文件有一些不同。首先，我们有几个额外的参数。即，缓存访问的延迟和缓存的大小。parameters-chapter一章更详细地介绍了这些类型的 SimObject 参数。</p>
<p>接下来，我们包含一个<code>System</code>参数，它是指向该缓存所连接的主系统的指针。这是必要的，因此我们可以在初始化缓存时从系统对象中获取缓存块大小。为了引用这个缓存所连接的系统对象，我们使用了一个特殊的<em>代理参数</em>。在这种情况下，我们使用<code>Parent.any</code>.</p>
<p>在 Python 配置文件中，当<code>SimpleCache</code>被实例化时，此代理参数会搜索<code>SimpleCache</code> 实例的所有父项以找到与该<code>System</code>类型匹配的 SimObject 。由于我们经常使用<code>System</code>作为根 SimObject，您经常会看到此代理参数被解析为 <code>system</code>。</p>
<p><code>SimpleCache</code>和 <code>SimpleMemobj</code>之间的第三个区别是：不同于有两个命名CPU端口（即<code>inst_port</code>和<code>data_port</code>），<code>SimpleCache</code>使用另一个特殊的参数：<code>VectorPort</code>。<code>VectorPorts</code>行为类似于常规端口（例如，它们由<code>getMasterPort</code>和<code>getSlavePort</code>解析），但它们允许此对象连接到多个对等点。然后，在解析函数中，我们之前忽略的参数 ( <code>PortID idx</code>) 用于区分不同的端口。通过使用向量端口，该缓存可以比 <code>SimpleMemobj</code>更灵活地连接系统.</p>
<h2 id="实现-simplecache"><a class="header" href="#实现-simplecache">实现 SimpleCache</a></h2>
<p><code>SimpleCache</code>的大部分代码与 <code>SimpleMemobj</code>相同。 构造函数和关键内存对象函数有一些变化。</p>
<p>首先，我们需要在构造函数中动态创建 CPU 侧端口，并根据 SimObject 参数初始化额外的成员函数。</p>
<pre><code class="language-cpp">SimpleCache::SimpleCache(SimpleCacheParams *params) :
    MemObject(params),
    latency(params-&gt;latency),
    blockSize(params-&gt;system-&gt;cacheLineSize()),
    capacity(params-&gt;size / blockSize),
    memPort(params-&gt;name + &quot;.mem_side&quot;, this),
    blocked(false), outstandingPacket(nullptr), waitingPortId(-1)
{
    for (int i = 0; i &lt; params-&gt;port_cpu_side_connection_count; ++i) {
        cpuPorts.emplace_back(name() + csprintf(&quot;.cpu_side[%d]&quot;, i), i, this);
    }
}
</code></pre>
<p>在这个函数中，我们使用系统参数中的<code>cacheLineSize</code>来设置缓存的<code>blockSize</code>。我们还根据块大小和参数初始化容量，并初始化我们下面需要的其他成员变量。最后，我们必须根据与此对象的连接数创建多个<code>CPUSidePorts</code> 。由于<code>cpu_side</code>端口在 SimObject Python 文件中声明为<code>VectorSlavePort</code> ，因此参数自动具有一个变量 <code>port_cpu_side_connection_count</code>. 这是基于参数的 Python 名称。对于这些连接中的每一个，我们向<code>SimpleCache</code>类中声明的<code>cpuPorts</code>向量添加一个新的<code>CPUSidePort</code>对象 。</p>
<p>我们还向<code>CPUSidePort</code>中添加了一个额外的成员变量以保存其 id，并将其作为参数添加到其构造函数中。</p>
<p>接下来，我们需要实现<code>getMasterPort</code>和<code>getSlavePort</code>。 <code>getMasterPort</code>与<code>SimpleMemobj</code>完全相同。对于 <code>getSlavePort</code>，我们现在需要根据请求的 id 返回端口。</p>
<pre><code class="language-cpp">BaseSlavePort&amp;
SimpleCache::getSlavePort(const std::string&amp; if_name, PortID idx)
{
    if (if_name == &quot;cpu_side&quot; &amp;&amp; idx &lt; cpuPorts.size()) {
        return cpuPorts[idx];
    } else {
        return MemObject::getSlavePort(if_name, idx);
    }
}
</code></pre>
<p>在<code>SimpleMemobj</code>中<code>CPUSidePort</code>和<code>MemSidePort</code>的实现的几乎相同。唯一的区别是我们需要向<code>handleRequest</code>添加一个额外的参数，即请求发起的端口的 id。如果没有这个 id，我们将无法将响应转发到正确的端口。<code>SimpleMemobj</code>根据原始请求是指令还是数据访问，得知要发送回复的端口。但是，此信息对<code>SimpleCache</code>无用， 因为它使用端口向量而不是命名端口。</p>
<p>新<code>handleRequest</code>函数与<code>SimpleMemobj</code>中的 <code>handleRequest</code>有两处不同。 首先，它存储如上所述的请求的端口 id。由于<code>SimpleCache</code>是阻塞的并且一次只允许一个未完成的请求，我们只需要保存一个端口 id。</p>
<p>其次，访问缓存需要时间。因此，我们需要考虑访问缓存标签和请求缓存数据的延迟。为此，我们向缓存对象添加了一个额外的参数，我们在<code>handleRequest</code>中使用一个事件将请求拖延所需的时间。我们为<code>latency</code>未来的周期安排了一个新的事件。<code>clockEdge</code>函数返回<em>n个周期后的</em>滴答数。</p>
<pre><code class="language-cpp">bool
SimpleCache::handleRequest(PacketPtr pkt, int port_id)
{
    if (blocked) {
        return false;
    }
    DPRINTF(SimpleCache, &quot;Got request for addr %#x\n&quot;, pkt-&gt;getAddr());

    blocked = true;
    waitingPortId = port_id;

    schedule(new AccessEvent(this, pkt), clockEdge(latency));

    return true;
}
</code></pre>
<p>这个<code>AccessEvent</code>比我们在event-chapter使用的<code>EventWrapper</code> 要复杂一些。在<code>SimpleCache</code>中我们将使用一个新类， 而不是<code>EventWrapper</code>，因为我们需要将数据包 ( <code>pkt</code>) 从<code>handleRequest</code>传递给事件处理函数。以下代码是 <code>AccessEvent</code>类。我们只需要实现<code>process</code>函数，以调用我们想要用作事件处理程序的函数，在本例中为<code>accessTming</code>。我们还将传递标志<code>AutoDelete</code>给事件构造函数，因此我们无需考虑为动态创建的对象释放内存。<code>process</code>函数执行后，事件代码会自动删除对象。</p>
<pre><code class="language-cpp">class AccessEvent : public Event
{
  private:
    SimpleCache *cache;
    PacketPtr pkt;
  public:
    AccessEvent(SimpleCache *cache, PacketPtr pkt) :
        Event(Default_Pri, AutoDelete), cache(cache), pkt(pkt)
    { }
    void process() override {
        cache-&gt;accessTiming(pkt);
    }
};
</code></pre>
<p>现在，我们需要实现事件处理程序<code>accessTiming</code>.</p>
<pre><code class="language-cpp">void
SimpleCache::accessTiming(PacketPtr pkt)
{
    bool hit = accessFunctional(pkt);
    if (hit) {
        pkt-&gt;makeResponse();
        sendResponse(pkt);
    } else {
        &lt;miss handling&gt;
    }
}
</code></pre>
<p>该函数首先在<em>功能上</em>访问缓存。此函数 <code>accessFunctional</code>（如下所述）执行缓存的功能访问，并在命中时读写缓存或返回访问未命中。</p>
<p>如果访问命中，我们只需要对数据包做出响应。要做出响应，您首先必须调用数据包上的函数<code>makeResponse</code>。这会将数据包从请求数据包转换为响应数据包。例如，如果数据包中的内存命令是<code>ReadReq</code>，它将被转换为<code>ReadResp</code>。写入行为类似。然后，我们可以将响应发送回 CPU。</p>
<p>除了使用<code>waitingPortId</code>将数据包发送到正确的端口之外，<code>sendResponse</code> 函数与<code>SimpleMemobj</code>中的<code>handleResponse</code>函数执行相同的操作。在这个函数中，我们需要在调用<code>sendPacket</code>前标记<code>SimpleCache</code>为unblocked ，以防CPU端的peer立即调用<code>sendTimingReq</code>。然后，如果<code>SimpleCache</code>现在可以接收请求，且端口需要重试发送，我们尝试向 CPU 端端口发送重试。</p>
<pre><code class="language-cpp">void SimpleCache::sendResponse(PacketPtr pkt)
{
    int port = waitingPortId;

    blocked = false;
    waitingPortId = -1;

    cpuPorts[port].sendPacket(pkt);
    for (auto&amp; port : cpuPorts) {
        port.trySendRetry();
    }
}
</code></pre>
<hr />
<p>回到<code>accessTiming</code>函数，我们现在需要处理缓存未命中的情况。如果未命中，我们首先必须检查丢失的数据包是否针对整个缓存块。如果数据包对齐并且请求的大小是缓存块的大小，那么我们可以简单地将请求转发到内存，就像在<code>SimpleMemobj</code>.</p>
<p>但是，如果数据包小于一个缓存块，那么我们需要创建一个新的数据包来从内存中读取整个缓存块。在这里，无论数据包是读请求还是写请求，我们都会向内存发送一个读请求，以将缓存块的数据加载到缓存中。如果是写请求，它会在我们从内存中加载数据后，在缓存中执行。</p>
<p>然后，我们创建一个新的数据包，大小与<code>blockSize</code>相同，我们在<code>Packet</code>对象中调用<code>allocate</code>函数为将从内存中读取的数据分配内存。注意：当我们释放数据包时，其内存被释放。我们使用数据包中的原始请求对象，以便内存侧对象统计请求发起者和请求类型。</p>
<p>最后，我们将发送方数据包指针 ( <code>pkt</code>)保存在一个成员变量<code>outstandingPacket</code>中，以便在<code>SimpleCache</code> 收到响应时可以恢复它。然后，我们通过内存端端口发送新数据包。</p>
<pre><code class="language-cpp">void
SimpleCache::accessTiming(PacketPtr pkt)
{
    bool hit = accessFunctional(pkt);
    if (hit) {
        pkt-&gt;makeResponse();
        sendResponse(pkt);
    } else {
        Addr addr = pkt-&gt;getAddr();
        Addr block_addr = pkt-&gt;getBlockAddr(blockSize);
        unsigned size = pkt-&gt;getSize();
        if (addr == block_addr &amp;&amp; size == blockSize) {
            DPRINTF(SimpleCache, &quot;forwarding packet\n&quot;);
            memPort.sendPacket(pkt);
        } else {
            DPRINTF(SimpleCache, &quot;Upgrading packet to block size\n&quot;);
            panic_if(addr - block_addr + size &gt; blockSize,
                     &quot;Cannot handle accesses that span multiple cache lines&quot;);

            assert(pkt-&gt;needsResponse());
            MemCmd cmd;
            if (pkt-&gt;isWrite() || pkt-&gt;isRead()) {
                cmd = MemCmd::ReadReq;
            } else {
                panic(&quot;Unknown packet type in upgrade size&quot;);
            }

            PacketPtr new_pkt = new Packet(pkt-&gt;req, cmd, blockSize);
            new_pkt-&gt;allocate();

            outstandingPacket = pkt;

            memPort.sendPacket(new_pkt);
        }
    }
}
</code></pre>
<p>根据内存的响应，我们知道这是由缓存未命中引起的。第一步是将响应数据包插入缓存中。</p>
<p>然后，要么有<code>outstandingPacket</code>，在这种情况下我们需要将该数据包转发给请求发起者，要么没有 <code>outstandingPacket</code>这意味着我们应该将响应中的<code>pkt</code>转发给请求发起者。</p>
<p>如果作为响应收到的数据包是更新数据包，因为发起的请求小于缓存行，那么我们需要将新数据复制到outstandingPacket 数据包或写入缓存。然后，我们需要删除我们在未命中处理逻辑中创建的新数据包。</p>
<pre><code class="language-cpp">bool
SimpleCache::handleResponse(PacketPtr pkt)
{
    assert(blocked);
    DPRINTF(SimpleCache, &quot;Got response for addr %#x\n&quot;, pkt-&gt;getAddr());
    insert(pkt);

    if (outstandingPacket != nullptr) {
        accessFunctional(outstandingPacket);
        outstandingPacket-&gt;makeResponse();
        delete pkt;
        pkt = outstandingPacket;
        outstandingPacket = nullptr;
    } // else, pkt contains the data it needs

    sendResponse(pkt);

    return true;
}
</code></pre>
<h3 id="功能缓存逻辑"><a class="header" href="#功能缓存逻辑">功能缓存逻辑</a></h3>
<p>现在，我们需要实现另外两个函数：<code>accessFunctional</code>和 <code>insert</code>。这两个函数构成了缓存逻辑的关键组件。</p>
<p>首先，为了在功能上更新缓存，我们首先需要存储缓存内容。最简单的缓存存储是从地址映射到数据的映射（哈希表）。因此，我们将以下成员添加到<code>SimpleCache</code>.</p>
<pre><code class="language-cpp">std::unordered_map&lt;Addr, uint8_t*&gt; cacheStore;
</code></pre>
<p>要访问缓存，我们首先检查映射中是否存在与数据包中的地址匹配的条目。我们使用<code>Packet</code>类中的<code>getBlockAddr</code> 函数来获取块对齐的地址。然后，我们只需在map中搜索该地址。如果我们没有找到地址，那么这个函数返回<code>false</code>，数据不在缓存中，就是未命中。</p>
<p>否则，如果数据包是写请求，我们需要更新缓存中的数据。为此，我们将数据包中的数据写入缓存。我们使用<code>writeDataToBlock</code>函数，将数据包中的数据写入到可能更大的缓存数据块。该函数采用缓存块偏移量和块大小（作为参数），并将正确的偏移量写入作为第一个参数传递的指针中。</p>
<p>如果数据包是读请求，我们需要用缓存中的数据更新数据包的数据。<code>setDataFromBlock</code>函数执行与<code>writeDataToBlock</code>函数相同的偏移量计算，但将第一个参数中指针中的数据写入数据包。</p>
<pre><code>bool
SimpleCache::accessFunctional(PacketPtr pkt)
{
    Addr block_addr = pkt-&gt;getBlockAddr(blockSize);
    auto it = cacheStore.find(block_addr);
    if (it != cacheStore.end()) {
        if (pkt-&gt;isWrite()) {
            pkt-&gt;writeDataToBlock(it-&gt;second, blockSize);
        } else if (pkt-&gt;isRead()) {
            pkt-&gt;setDataFromBlock(it-&gt;second, blockSize);
        } else {
            panic(&quot;Unknown packet type!&quot;);
        }
        return true;
    }
    return false;
}
</code></pre>
<p>最后，我们还需要实现该<code>insert</code>功能。每次内存端端口响应请求时都会调用此函数。</p>
<p>第一步是检查缓存当前是否已满。如果缓存的条目（块）比 SimObject 参数设置的缓存容量多，那么我们需要替换一些东西。以下代码通过利用 C++ 的<code>unordered_map</code>哈希表实现来随机替换条目。</p>
<p>在置换时，我们需要将数据写回后备内存，以防它已被更新。为此，我们创建了一个新的<code>Request</code>-<code>Packet</code> 对。数据包使用了一个新的内存命令：<code>MemCmd::WritebackDirty</code>。然后，我们通过内存端端口 ( <code>memPort</code>)发送数据包并擦除缓存存储映射中的条目。</p>
<p>然后，在一个块可能被驱逐后，我们将新地址添加到缓存中。为此，我们只需为块分配空间并向映射添加一个条目。最后，我们将响应包中的数据写入新分配的块中。可以认为这个数据包等于缓存块的大小，因为如果数据包小于等于缓存块，我们要在缓存未命中逻辑中创建一个新数据包。</p>
<pre><code class="language-cpp">void
SimpleCache::insert(PacketPtr pkt)
{
    if (cacheStore.size() &gt;= capacity) {
        // Select random thing to evict. This is a little convoluted since we
        // are using a std::unordered_map. See http://bit.ly/2hrnLP2
        int bucket, bucket_size;
        do {
            bucket = random_mt.random(0, (int)cacheStore.bucket_count() - 1);
        } while ( (bucket_size = cacheStore.bucket_size(bucket)) == 0 );
        auto block = std::next(cacheStore.begin(bucket),
                               random_mt.random(0, bucket_size - 1));

        RequestPtr req = new Request(block-&gt;first, blockSize, 0, 0);
        PacketPtr new_pkt = new Packet(req, MemCmd::WritebackDirty, blockSize);
        new_pkt-&gt;dataDynamic(block-&gt;second); // This will be deleted later

        DPRINTF(SimpleCache, &quot;Writing packet back %s\n&quot;, pkt-&gt;print());
        memPort.sendTimingReq(new_pkt);

        cacheStore.erase(block-&gt;first);
    }
    uint8_t *data = new uint8_t[blockSize];
    cacheStore[pkt-&gt;getAddr()] = data;

    pkt-&gt;writeDataToBlock(data, blockSize);
}
</code></pre>
<h2 id="为缓存创建配置文件"><a class="header" href="#为缓存创建配置文件">为缓存创建配置文件</a></h2>
<p>我们实现的最后一步是创建一个使用我们缓存的新 Python 配置脚本。我们可以使用<a href="https://www.gem5.org/documentation/learning_gem5/part2/memoryobject">上一章</a>的大纲 作为起点。唯一的区别是我们可能想要设置此缓存的参数（例如，将缓存的大小设置为<code>1kB</code>），而不是使用命名端口（<code>data_port</code>和<code>inst_port</code>），我们只使用该<code>cpu_side</code>端口两次。由于<code>cpu_side</code>是 a <code>VectorPort</code>，它将自动创建多个端口连接。</p>
<pre><code class="language-python">import m5
from m5.objects import *

...

system.cache = SimpleCache(size='1kB')

system.cpu.icache_port = system.cache.cpu_side
system.cpu.dcache_port = system.cache.cpu_side

system.membus = SystemXBar()

system.cache.mem_side = system.membus.slave

...
</code></pre>
<p>Python 配置文件可以在<a href="https://www.gem5.org/_pages/static/scripts/part2/simplecache/simple_cache.py">这里</a>下载 。</p>
<p>运行此脚本应该会从 hello 二进制文件中产生预期的输出。</p>
<pre><code class="language-bash">gem5 Simulator System.  http://gem5.org
gem5 is copyrighted software; use the --copyright option for details.

gem5 compiled Jan 10 2017 17:38:15
gem5 started Jan 10 2017 17:40:03
gem5 executing on chinook, pid 29031
command line: build/X86/gem5.opt configs/learning_gem5/part2/simple_cache.py

Global frequency set at 1000000000000 ticks per second
warn: DRAM device capacity (8192 Mbytes) does not match the address range assigned (512 Mbytes)
0: system.remote_gdb.listener: listening for remote gdb #0 on port 7000
warn: CoherentXBar system.membus has no snooping ports attached!
warn: ClockedObject: More than one power state change request encountered within the same simulation tick
Beginning simulation!
info: Entering event queue @ 0.  Starting simulation...
Hello world!
Exiting @ tick 56082000 because target called exit()
</code></pre>
<p>修改缓存的大小，例如修改为 128 KB，应该可以提高系统的性能。</p>
<pre><code class="language-bash">gem5 Simulator System.  http://gem5.org
gem5 is copyrighted software; use the --copyright option for details.

gem5 compiled Jan 10 2017 17:38:15
gem5 started Jan 10 2017 17:41:10
gem5 executing on chinook, pid 29037
command line: build/X86/gem5.opt configs/learning_gem5/part2/simple_cache.py

Global frequency set at 1000000000000 ticks per second
warn: DRAM device capacity (8192 Mbytes) does not match the address range assigned (512 Mbytes)
0: system.remote_gdb.listener: listening for remote gdb #0 on port 7000
warn: CoherentXBar system.membus has no snooping ports attached!
warn: ClockedObject: More than one power state change request encountered within the same simulation tick
Beginning simulation!
info: Entering event queue @ 0.  Starting simulation...
Hello world!
Exiting @ tick 32685000 because target called exit()
</code></pre>
<h2 id="向缓存添加统计信息"><a class="header" href="#向缓存添加统计信息">向缓存添加统计信息</a></h2>
<p>了解系统的整体执行时间是一项重要指标。但是，您可能还想包括其他统计信息，例如缓存的命中率和未命中率。为此，我们需要向<code>SimpleCache</code>对象添加一些统计信息。</p>
<p>首先，我们需要在<code>SimpleCache</code>对象中声明统计信息。它们是<code>Stats</code>命名空间的一部分。本例中，我们将进行四项统计。<code>hits</code>的数量和<code>misses</code>的数量只是简单的<code>Scalar</code>计数。我们还将添加  <code>missLatency</code>，它是缓存未命中所需访问时间的直方图。最后，我们给<code>hitRatio</code>添加一个特殊统计数据<code>Formula</code>，它是其他统计数据（命中和未命中的数量）的组合。</p>
<pre><code class="language-cpp">class SimpleCache : public MemObject
{
  private:
    ...

    Tick missTime; // To track the miss latency

    Stats::Scalar hits;
    Stats::Scalar misses;
    Stats::Histogram missLatency;
    Stats::Formula hitRatio;

  public:
    ...

    void regStats() override;
};
</code></pre>
<p>接下来，我们必须重写<code>regStats</code>函数，以便将统计信息注册到 gem5 的统计基础架构中。在这里，对于每个统计数据，我们根据“父” SimObject 名称和描述为其命名。对于直方图统计，我们还要用桶数来初始化它。最后，我们只需要在代码中写下公式即可。</p>
<pre><code class="language-cpp">void
SimpleCache::regStats()
{
    // If you don't do this you get errors about uninitialized stats.
    MemObject::regStats();

    hits.name(name() + &quot;.hits&quot;)
        .desc(&quot;Number of hits&quot;)
        ;

    misses.name(name() + &quot;.misses&quot;)
        .desc(&quot;Number of misses&quot;)
        ;

    missLatency.name(name() + &quot;.missLatency&quot;)
        .desc(&quot;Ticks for misses to the cache&quot;)
        .init(16) // number of buckets
        ;

    hitRatio.name(name() + &quot;.hitRatio&quot;)
        .desc(&quot;The ratio of hits to the total accesses to the cache&quot;)
        ;

    hitRatio = hits / (hits + misses);

}
</code></pre>
<p>最后，我们需要在我们的代码中使用更新统计信息。在 <code>accessTiming</code>类中，我们可以分别在命中和未命中时增加<code>hits</code>和<code>misses</code>。此外，如果出现未命中，我们会保存当前时间，以便我们可以测量延迟。</p>
<pre><code class="language-cpp">void
SimpleCache::accessTiming(PacketPtr pkt)
{
    bool hit = accessFunctional(pkt);
    if (hit) {
        hits++; // update stats
        pkt-&gt;makeResponse();
        sendResponse(pkt);
    } else {
        misses++; // update stats
        missTime = curTick();
        ...
</code></pre>
<p>然后，当我们得到响应时，我们需要将测量的延迟添加到我们的直方图中。为此，我们使用<code>sample</code>函数。这会在直方图中添加一个点。此直方图会自动调整桶的大小以适应它接收到的数据。</p>
<pre><code class="language-cpp">bool
SimpleCache::handleResponse(PacketPtr pkt)
{
    insert(pkt);

    missLatency.sample(curTick() - missTime);
    ...
</code></pre>
<p><code>SimpleCache</code>头文件的完整代码可以在<a href="https://www.gem5.org/_pages/static/scripts/part2/simplecache/simple_cache.hh">这里</a>下载 ，<code>SimpleCache</code>实现的完整代码可以在<a href="https://www.gem5.org/_pages/static/scripts/part2/simplecache/simple_cache.cc">这里</a>下载 。</p>
<p>现在，如果我们运行上面的配置文件，我们可以检查<code>stats.txt</code>文件中的统计信息。对于 1 KB 的情况，我们得到以下统计信息。访存命中率为91% ，平均未命中延迟为 53334 滴答（或 53 ns）。</p>
<pre><code class="language-bash">system.cache.hits                                8431                       # Number of hits
system.cache.misses                               877                       # Number of misses
system.cache.missLatency::samples                 877                       # Ticks for misses to the cache
system.cache.missLatency::mean           53334.093501                       # Ticks for misses to the cache
system.cache.missLatency::gmean          44506.409356                       # Ticks for misses to the cache
system.cache.missLatency::stdev          36749.446469                       # Ticks for misses to the cache
system.cache.missLatency::0-32767                 305     34.78%     34.78% # Ticks for misses to the cache
system.cache.missLatency::32768-65535             365     41.62%     76.40% # Ticks for misses to the cache
system.cache.missLatency::65536-98303             164     18.70%     95.10% # Ticks for misses to the cache
system.cache.missLatency::98304-131071             12      1.37%     96.47% # Ticks for misses to the cache
system.cache.missLatency::131072-163839            17      1.94%     98.40% # Ticks for misses to the cache
system.cache.missLatency::163840-196607             7      0.80%     99.20% # Ticks for misses to the cache
system.cache.missLatency::196608-229375             0      0.00%     99.20% # Ticks for misses to the cache
system.cache.missLatency::229376-262143             0      0.00%     99.20% # Ticks for misses to the cache
system.cache.missLatency::262144-294911             2      0.23%     99.43% # Ticks for misses to the cache
system.cache.missLatency::294912-327679             4      0.46%     99.89% # Ticks for misses to the cache
system.cache.missLatency::327680-360447             1      0.11%    100.00% # Ticks for misses to the cache
system.cache.missLatency::360448-393215             0      0.00%    100.00% # Ticks for misses to the cache
system.cache.missLatency::393216-425983             0      0.00%    100.00% # Ticks for misses to the cache
system.cache.missLatency::425984-458751             0      0.00%    100.00% # Ticks for misses to the cache
system.cache.missLatency::458752-491519             0      0.00%    100.00% # Ticks for misses to the cache
system.cache.missLatency::491520-524287             0      0.00%    100.00% # Ticks for misses to the cache
system.cache.missLatency::total                   877                       # Ticks for misses to the cache
system.cache.hitRatio                        0.905780                       # The ratio of hits to the total access
</code></pre>
<p>当使用 128 KB 缓存时，我们获得了略高的命中率。看起来我们的缓存按预期工作！</p>
<pre><code class="language-bash">system.cache.hits                                8944                       # Number of hits
system.cache.misses                               364                       # Number of misses
system.cache.missLatency::samples                 364                       # Ticks for misses to the cache
system.cache.missLatency::mean           64222.527473                       # Ticks for misses to the cache
system.cache.missLatency::gmean          61837.584812                       # Ticks for misses to the cache
system.cache.missLatency::stdev          27232.443748                       # Ticks for misses to the cache
system.cache.missLatency::0-32767                   0      0.00%      0.00% # Ticks for misses to the cache
system.cache.missLatency::32768-65535             254     69.78%     69.78% # Ticks for misses to the cache
system.cache.missLatency::65536-98303             106     29.12%     98.90% # Ticks for misses to the cache
system.cache.missLatency::98304-131071              0      0.00%     98.90% # Ticks for misses to the cache
system.cache.missLatency::131072-163839             0      0.00%     98.90% # Ticks for misses to the cache
system.cache.missLatency::163840-196607             0      0.00%     98.90% # Ticks for misses to the cache
system.cache.missLatency::196608-229375             0      0.00%     98.90% # Ticks for misses to the cache
system.cache.missLatency::229376-262143             0      0.00%     98.90% # Ticks for misses to the cache
system.cache.missLatency::262144-294911             2      0.55%     99.45% # Ticks for misses to the cache
system.cache.missLatency::294912-327679             1      0.27%     99.73% # Ticks for misses to the cache
system.cache.missLatency::327680-360447             1      0.27%    100.00% # Ticks for misses to the cache
system.cache.missLatency::360448-393215             0      0.00%    100.00% # Ticks for misses to the cache
system.cache.missLatency::393216-425983             0      0.00%    100.00% # Ticks for misses to the cache
system.cache.missLatency::425984-458751             0      0.00%    100.00% # Ticks for misses to the cache
system.cache.missLatency::458752-491519             0      0.00%    100.00% # Ticks for misses to the cache
system.cache.missLatency::491520-524287             0      0.00%    100.00% # Ticks for misses to the cache
system.cache.missLatency::total                   364                       # Ticks for misses to the cache
system.cache.hitRatio                        0.960894                       # The ratio of hits to the total access
</code></pre>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                            <a rel="prev" href="../part2/part2_5_memoryobject.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                                <i class="fa fa-angle-left"></i>
                            </a>
                            <a rel="next" href="../part2/part2_7_arm_power_modelling.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                                <i class="fa fa-angle-right"></i>
                            </a>
                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
                    <a rel="prev" href="../part2/part2_5_memoryobject.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>
                    <a rel="next" href="../part2/part2_7_arm_power_modelling.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
            </nav>

        </div>

        <script type="text/javascript">
            window.playground_copyable = true;
        </script>
        <script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
        <script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="../book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->
    </body>
</html>
